「提高 - 59」基础莫队
莫队
莫队算法是一种对询问进行分块的离线算法。
具体步骤
设询问的区间均位于 。
我们将 分为 块,每块的长度为 。
对所有的询问按照左端点进行排序,左端点位于同一块(上面的分块)的询问属于同一组,我们再对每一组内部按照右端点升序排列。
int len = sqrt(n);
sort(p + 1, p + m + 1, [len](const node &a, const node &b) {
int x = a.l / len, y = b.l / len;
if (x == y) return a.r < b.r;
return x < y;
});
对排序后的询问,我们暴力计算从 变为区间 ,对答案的影响。如果能够以 的代价计算出 转化为 、、、。那么从 更新为区间 的代价便是左右端点的变化之和。因为每一块内,左端点每次最多变化 ,所有 次询问的总代价为 。右端点因为在每块内部从 一直递增,最多变为 。一共有 块,所以总共的时间复杂度 。
下面的代码是查询区间和。
int l = 1, r = 0;
int sum = 0;
for (int i = 1; i <= m; i++) {
while (l > p[i].l) sum += A[--l];
while (r < p[i].r) sum += A[++r];
while (l < p[i].l) sum -= A[l++];
while (r > p[i].r) sum -= A[r--];
Ans[p[i].id] = sum;
}
在移动区间端点时,建议先执行扩张操作,再执行收缩操作。这样可以保证每次删除的元素一定已经被加入过当前区间,避免在区间临时变空或左右端点交错时,对未加入过的元素执行删除操作,导致计数数组或答案状态出错。
比如说上一个区间为 , 当前区间为 。如果我们先收缩区间的话,会出现把 从区间删除的操作,在查询区间和虽然不会出问题,但是在另一些问题中就可能影响答案或程序运行错误。
合理的步骤是先将 扩充为 ,再收缩为 。